﻿using System;
using System.Diagnostics;
using System.Reactive.Linq;
using AlanAamy.Net.TemperatureConverter.Infrastructure.Constants;
using AlanAamy.Net.TemperatureConverter.Models;
using AlanAamy.Net.TemperatureConverter.Services;
using Microsoft.Reactive.Testing;
using Moq;
using NUnit.Framework;

namespace AlanAamy.Net.TemperatureConverter.Tests
{
    [TestFixture]
    public sealed class SequenceObserverControllerTest
    {
        private TestScheduler _testScheduler;
        private Mock<ILeftSequenceObserverModel> _mockLeftObservableModel;
        private Mock<IRightSequenceObserverModel> _mockRightObservableModel;
        private int _onNextRightObservedCount;
        private int _onNextLeftObserverCount;

        [SetUp]
        public void Setup()
        {
            _testScheduler = new TestScheduler();
            _mockRightObservableModel = new Mock<IRightSequenceObserverModel>();
            _mockLeftObservableModel = new Mock<ILeftSequenceObserverModel>();
            _onNextRightObservedCount = 0;
            _mockLeftObservableModel.Setup(x => x.GetObservable()).Returns(
                () =>
                {
                    return Observable.Create<Decimal>(o =>
                    {
                        ++_onNextRightObservedCount; //Debug.WriteLine will not be compiled in release
                        Debug.WriteLine("OnNext => RightObserved Count {0}", _onNextRightObservedCount);
                        return Observable.Never<Decimal>().StartWith(1M).Subscribe(o);
                    });
                });

            _mockRightObservableModel.Setup(x => x.GetObservable()).Returns(
                () =>
                {
                    return Observable.Create<Decimal>(o =>
                    {
                        ++_onNextLeftObserverCount;
                        Debug.WriteLine("OnNext => LeftObserved {0}", _onNextLeftObserverCount);
                        return Observable.Never<Decimal>().StartWith(1M).Subscribe(o);
                    });
                });

        }

        [Test]
        public void ToggleUnWireSequenceObserversCallsToggleUnitOnDataModel()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);

            //Act
            sequenceControllerService.ToggleUnWireSequenceObservers();

            //Assert
            _mockRightObservableModel.Verify(_ => _.ToggleUnit());
            _mockLeftObservableModel.Verify(_ => _.ToggleUnit());
        }

        [Test]
        public void ToggleUnWireSequenceObserversChangesValueOfRightModel()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 1M);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 0M);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);

            //Act
            sequenceControllerService.ToggleUnWireSequenceObservers();

            //Assert
            Assert.AreEqual(33.8M, _mockRightObservableModel.Object.Value);
        }

        [Test]
        public void ToggleUnWireSequenceObserversDoNotChangeValueOfLeftModel()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 1M);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 0M);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);

            //Act
            sequenceControllerService.ToggleUnWireSequenceObservers();

            //Assert
            Assert.AreEqual(1M, _mockLeftObservableModel.Object.Value);
        }

        [Test]
        public void ToggleUnWireSequenceObserversUnWiresBothObservers()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.LeftToRight, TemperatureUnit.Celsius, _testScheduler,_testScheduler);
            sequenceControllerService.ToggleUnWireSequenceObservers();
            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.GreaterOrEqual(1, _onNextRightObservedCount);

            //Arrange
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.LeftToRight, TemperatureUnit.Celsius, _testScheduler, _testScheduler); 
            sequenceControllerService.ToggleUnWireSequenceObservers();

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.GreaterOrEqual(1, _onNextRightObservedCount);          
        }

        [Test]
        public void RewireSequenceObserversUnWiresLeftObserverInModeLeftToRight()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.LeftToRight, TemperatureUnit.Celsius, _testScheduler, _testScheduler);
            _onNextRightObservedCount = 0;
            _onNextLeftObserverCount = 0;

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.GreaterOrEqual(_onNextRightObservedCount,1);
            Assert.GreaterOrEqual(_onNextLeftObserverCount,0 );            
        }

        [Test]
        public void RewireSequenceObserversUnWiresRightObserverInModeRightToLeft()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.RightToLeft, TemperatureUnit.Celsius, _testScheduler, _testScheduler);
            _onNextRightObservedCount = 0;
            _onNextLeftObserverCount = 0;

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.GreaterOrEqual(_onNextRightObservedCount, 0);
            Assert.GreaterOrEqual(_onNextLeftObserverCount, 1);
        }

        [Test]
        public void RewireSequenceObserversRightToLeftFailsWhenCurrentUnitIsDifferent()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.RightToLeft, TemperatureUnit.Celsius, _testScheduler, _testScheduler);
            _onNextRightObservedCount = 0;
            _onNextLeftObserverCount = 0;

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.GreaterOrEqual(_onNextRightObservedCount, 0);
            Assert.GreaterOrEqual(_onNextLeftObserverCount, 0); //Note we have no NextLeftObserverCount 
        }

        [Test]
        public void RewireSequenceObserversLeftToRightFailsWhenCurrentUnitIsDifferent()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.LeftToRight, 
                TemperatureUnit.Celsius, _testScheduler, _testScheduler);
            _onNextRightObservedCount = 0;
            _onNextLeftObserverCount = 0;

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.GreaterOrEqual(_onNextRightObservedCount, 0);
            Assert.GreaterOrEqual(_onNextLeftObserverCount, 0); //Note we have no NextLeftObserverCount 
        }

        [Test]
        public void RewireSequenceObserversChangesValueOfRightModelInModeLeftToRight()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 1M);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 0M);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.LeftToRight, TemperatureUnit.Celsius, _testScheduler, _testScheduler);
            _onNextRightObservedCount = 0;
            _onNextLeftObserverCount = 0;

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.AreEqual(33.8M, _mockRightObservableModel.Object.Value);
        }

        [Test]
        public void RewireSequenceObserversChangesValueOfLeftModelInModeRightToLeft()
        {
            //Arrange
            var sequenceControllerService = new SequenceObserverController(_mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 0M);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 33.8M);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);
            sequenceControllerService.RewireSequenceObservers(UnitConversionMode.RightToLeft, TemperatureUnit.Farenheit, _testScheduler, _testScheduler);
            _onNextRightObservedCount = 0;
            _onNextLeftObserverCount = 0;

            //Act
            _testScheduler.AdvanceBy(TimeSpan.FromHours(1).Ticks);

            //Assert
            Assert.AreEqual(33.8M, _mockRightObservableModel.Object.Value);
        }


    }
}
